Add support for PEP 661 (sentinels)#21647
Conversation
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
Signed-off-by: Edgar Ramírez Mondragón <edgarrm358@gmail.com>
Signed-off-by: Edgar Ramírez Mondragón <edgarrm358@gmail.com>
This comment has been minimized.
This comment has been minimized.
Signed-off-by: Edgar Ramírez Mondragón <edgarrm358@gmail.com>
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
d-v-b's call: PEP 661 is Final, ty already types the sentinel spelling exactly, mypy support is in review (python/mypy#21647) and treated as imminent, and pyright has a known-good version — so use the standard sentinel today rather than carrying the enum stopgap. - UNSET is now typing_extensions.Sentinel("UNSET"), used directly in type expressions (tuple[str | None, ...] | UNSET); the UnsetType companion enum is gone from the API. - typing_extensions floor bumped to 4.14 (where Sentinel arrived). - CI pins pyright==1.1.404, the last version before the class-attribute sentinel regression (microsoft/pyright#11115); pyproject documents the same pin for local runs. 0 errors on the pin; ty checks the sentinel fields clean (its 2 remaining diagnostics are its incomplete PEP 728 extra_items write support, unrelated). - Known short-term cost, accepted deliberately: mypy-checked consumers need cast/type-ignore at narrowing sites until mypy#21647 merges, and contributors' Pylance may show phantom Unknowns until the pyright fix ships. Recorded in _sentinel.py and the changelog. - The pydantic native-introspection test reverts to documenting that introspection is unsupported (pydantic 2.13 cannot schema a Sentinel); the delegation patterns are unaffected. Assisted-by: ClaudeCode:claude-fable-5
The zarr-metadata model layer that zarr now depends on is unreleased, and
the previous wiring only covered uv project flows, breaking everything else:
- The floor 'zarr-metadata>=0.4.0' is unresolvable from PyPI (only <0.4.0
exists), failing every hatch env (test matrix, doctests, benchmarks,
hypothesis, min_deps, upstream) and the readthedocs build. The floor now
names the newest release (>=0.3) with a TODO to bump at release time, and
each non-uv flow installs the in-tree package explicitly: hatch envs get a
'{root:uri}/packages/zarr-metadata' direct reference (repeated per env,
since hatch replaces extra-dependencies on inheritance), readthedocs gets
an extra pip install step.
- uv workspace membership made 'uv sync' inside packages/zarr-metadata
resolve the whole workspace, dragging zarr's requires-python (>=3.12) into
the subpackage's own CI, which tests down to 3.11. The workspace is now a
plain editable path source, restoring the subpackage's independence.
- min_deps pins typing_extensions==4.16.* (was 4.14.*): 4.16 is the
effective minimum because zarr-metadata requires it for sentinel pickling.
Also fixes the Lint (mypy) findings in the new tests — a bare generic Array
annotation, stale type-ignores, and two sentinel comparisons mypy cannot
narrow (python/mypy#21647) — and renames the changelog entries to the PR
number (4129) required by the changelog check.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
You'll need to edit mypy/server/deps.py to properly record some dependencies. Codex found this which seems correct:
A sentinel literal now contains the defining symbol’s fullname, but
[visit_literal_type()](https://github.com/python/mypy/blob/acfbf461408a091ca4494e0c0ac7eba9aded35c1/mypy/server/deps.py#L1091-L1092)only records the fallback-type dependency. In a reproducer wheremod.MISSINGchanges fromSentinel("MISSING")to1, the daemon does not reanalyzedef f(x: MISSING). It retains the stale sentinel annotation and reports that the integer argument is incompatible, rather than reporting thatmod.MISSINGis no longer a valid type. The visitor should add a trigger for[SentinelValue.fullname](https://github.com/python/mypy/blob/acfbf461408a091ca4494e0c0ac7eba9aded35c1/mypy/types.py#L101-L105).
| - name: Setup tox environment | ||
| run: | | ||
| tox run -e ${{ matrix.toxenv }} --notest | ||
| - name: Install local librt in tox environment |
There was a problem hiding this comment.
We'll need a coordinated librt release in order to publish this. I don't believe I have the powers to do that.
There was a problem hiding this comment.
Yeah, I think someone with write access to the librt repo needs to run a script once this PR lands on master
| return not rv.node.is_enum | ||
| if isinstance(rv.node, Var): | ||
| if rv.node.is_sentinel: | ||
| return True |
There was a problem hiding this comment.
This is incorrect, it causes us to treat every sentinel reassignment as an alias.
Something like this:
MISSING = Sentinel("MISSING")
ALIAS = MISSING
There was a problem hiding this comment.
Gotcha, given this text from the PEP:
If the name passed to
sentinel()does not match the name the object is assigned to, type checkers should emit an error.
should this cause mypy to fail then?
MISSING = Sentinel("MISSING")
ALIAS = MISSING # error: ...?There was a problem hiding this comment.
We're likely dropping that requirement from the spec. I think this should be fine.
|
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅ |
See PEP 661 and python/typing#2277